Skip to main content

libobs_wrapper\display\window_manager/
position_trait.rs

1#[cfg(windows)]
2use windows::Win32::{
3    Foundation::HWND,
4    Graphics::Gdi::{RedrawWindow, RDW_ERASE, RDW_INVALIDATE},
5    UI::WindowsAndMessaging::{
6        SetWindowPos, HWND_BOTTOM, SWP_NOACTIVATE, SWP_NOCOPYBITS, SWP_NOSIZE, SWP_NOZORDER,
7        SWP_SHOWWINDOW,
8    },
9};
10
11use crate::display::window_manager::WindowPositionTrait;
12use crate::utils::ObsError;
13use crate::{display::ObsDisplayRef, run_with_obs};
14
15impl WindowPositionTrait for ObsDisplayRef {
16    fn set_render_at_bottom(&self, _render_at_bottom: bool) -> Result<(), ObsError> {
17        log::trace!("Set render bottom");
18        #[cfg(windows)]
19        if let Some(m) = self.child_window_handler.as_ref() {
20            let mut m = m
21                .write()
22                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
23
24            m.render_at_bottom = _render_at_bottom;
25        }
26        Ok(())
27    }
28
29    fn get_render_at_bottom(&self) -> Result<bool, ObsError> {
30        #[cfg(windows)]
31        if let Some(m) = self.child_window_handler.as_ref() {
32            let m = m
33                .read()
34                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
35            return Ok(m.render_at_bottom);
36        }
37
38        Ok(false)
39    }
40
41    fn set_pos(&self, x: i32, y: i32) -> Result<(), ObsError> {
42        log::trace!("Set pos {x} {y}");
43
44        #[cfg(windows)]
45        if let Some(m) = self.child_window_handler.as_ref() {
46            let mut m = m
47                .write()
48                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
49
50            if !m.has_display_handle() {
51                return Err(ObsError::InvalidOperation(
52                    "preview child has no OBS display handle".to_string(),
53                ));
54            }
55
56            let insert_after = if m.render_at_bottom {
57                HWND_BOTTOM
58            } else {
59                HWND::default()
60            };
61
62            m.x = x;
63            m.y = y;
64
65            unsafe {
66                // SAFETY: The window handle is valid because it was created and stored in the struct during initialization.
67                // SetWindowPos is a safe Windows API call when provided with a valid window handle and valid parameters.
68                let flags = SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE;
69                // Just use dummy values as size is not changed
70                SetWindowPos(
71                    m.window_handle.get_hwnd(),
72                    Some(insert_after),
73                    x,
74                    y,
75                    1_i32,
76                    1_i32,
77                    flags,
78                )
79                .map_err(|e| ObsError::DisplayCreationError(format!("{:?}", e)))?;
80            }
81
82            // Update color space when window position changes
83            drop(m); // Release the lock before calling run_with_obs
84            self.update_color_space()?;
85            return Ok(());
86        }
87
88        *self.render_state.position.write().map_err(|_| {
89            ObsError::LockError("display render position lock poisoned".to_string())
90        })? = (x, y);
91
92        self.update_color_space()?;
93        Ok(())
94    }
95
96    fn set_size(&self, width: u32, height: u32) -> Result<(), ObsError> {
97        log::trace!("Set size {width} {height}");
98
99        #[cfg(windows)]
100        if let Some(m) = self.child_window_handler.as_ref() {
101            let mut m = m
102                .write()
103                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
104
105            if !m.has_display_handle() {
106                return Err(ObsError::InvalidOperation(
107                    "preview child has no OBS display handle".to_string(),
108                ));
109            }
110
111            m.width = width;
112            m.height = height;
113
114            unsafe {
115                // SAFETY: The window handle is valid because it was created and stored in the struct during initialization.
116                // SetWindowPos is a Windows API call that is safe when provided with a valid window handle and valid parameters.
117                SetWindowPos(
118                    m.window_handle.get_hwnd(),
119                    None,
120                    m.x,
121                    m.y,
122                    width as i32,
123                    height as i32,
124                    SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW,
125                )
126                .map_err(|e| ObsError::DisplayCreationError(format!("{:?}", e)))?;
127
128                let _ = RedrawWindow(
129                    Some(m.window_handle.get_hwnd()),
130                    None,
131                    None,
132                    RDW_ERASE | RDW_INVALIDATE,
133                );
134            }
135        }
136
137        let pointer = self.as_ptr();
138        run_with_obs!(self.runtime, (pointer), move || {
139            unsafe {
140                // Safety: The pointer is valid because we are using a smart pointer
141
142                libobs::obs_display_resize(pointer.get_ptr(), width, height);
143                // Update color space when window size changes
144                libobs::obs_display_update_color_space(pointer.get_ptr());
145            }
146        })?;
147        Ok(())
148    }
149
150    fn get_pos(&self) -> Result<(i32, i32), ObsError> {
151        #[cfg(windows)]
152        if let Some(m) = self.child_window_handler.as_ref() {
153            let m = m
154                .read()
155                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
156            return Ok((m.x, m.y));
157        }
158
159        let pos = self.render_state.position.read().map_err(|_| {
160            ObsError::LockError("display render position lock poisoned".to_string())
161        })?;
162        Ok(*pos)
163    }
164
165    fn get_size(&self) -> Result<(u32, u32), ObsError> {
166        #[cfg(windows)]
167        if let Some(m) = self.child_window_handler.as_ref() {
168            let m = m
169                .read()
170                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
171
172            return Ok((m.width, m.height));
173        }
174
175        let pointer = self.as_ptr();
176        let (width, height) = run_with_obs!(self.runtime, (pointer), move || {
177            let mut w: u32 = 0;
178            let mut h: u32 = 0;
179            unsafe {
180                // Safety: The pointer is valid because we are using a smart pointer and because `w` and `h` are valid mutable references.
181                libobs::obs_display_size(pointer.get_ptr(), &mut w, &mut h);
182            };
183
184            (w, h)
185        })?;
186
187        Ok((width, height))
188    }
189}